﻿///<reference path="freenode.ts" />
namespace FreeNode.ForEgret3D
{

    //基于场景图对象的骨骼
    export class Skinner implements egret3d.IAnimation
    {
        constructor(bones: string[], rootBoneObj: Entity_Mesh)
        {
            this.rootBoneObj = rootBoneObj;
            if (rootBoneObj != null)
                this.player = rootBoneObj.aniplayer;
            this.bones = bones;

            this._skeletonMatrixData = new Float32Array(this.bones.length * 8);
            for (var i: number = 0; i < this.bones.length; i++)
            {
                this._skeletonMatrixData[i * 8 + 3] = 1;//x y z w =0 0 0 1
                this._skeletonMatrixData[i * 8 + 7] = 1;//x y z w =0 0 0 1
            }
            this.skeletonAnimationController = <egret3d.SkeletonAnimation>{};
            var obj: any = this.skeletonAnimationController;
            obj["jointNum"] = this.bones.length;//hack this num.

            //this.skeletonAnimationController.jointNum = this.bones.length;
            //new SkinnerFormat(this.bones.length);
        }
        private _skeletonMatrixData: Float32Array;
        bones: string[];
        rootBoneObj: Entity_Mesh;
        /**
        * @language zh_CN
        * 一个完整的动画播放时间周期
        * @version Egret 3.0
        * @platform Web,Native
        */
        loopTime: number;
        /**
        * @language zh_CN
        * 是否为一个循环播放的动画
        * @version Egret 3.0
        * @platform Web,Native
        */
        isLoop: boolean;
        player: AniPlayer = null;
        static _findPlayerObj(root: Entity_Mesh | Entity_Empty)
        {

            if (root instanceof (Entity_Mesh))
            {
                var mr: Entity_Mesh = <Entity_Mesh>root;
                if (mr.aniplayer != null)
                {
                    return mr;
                }
            }
            if (root.parent instanceof (Entity_Mesh) || root.parent instanceof (Entity_Empty))
            {
                return Skinner._findPlayerObj(<any>root.parent);
            }
            return null;
        }
        clone(): egret3d.IAnimation
        {
            return new Skinner(this.bones, this.rootBoneObj);
        }
        fix(root: Entity_Mesh)
        {
            var pobj = Skinner._findPlayerObj(root);
            this.rootBoneObj = pobj;
            this.player = this.rootBoneObj.aniplayer;
        }
        //占位，补材质
        skeletonAnimationController: egret3d.SkeletonAnimation = null;


        update(time: number, delay: number, geometry: egret3d.Geometry): void
        {

            //if (this.player == null)
            //    this._findPlayer(this.rootBoneObj);
            if (this.player != null)
            {
                this.player.fillPoseData(this._skeletonMatrixData, this.bones);
            }
            this.animTime += delay * this.speed;
        }
        activeState(time: number, delay: number, usage: egret3d.PassUsage, geometry: egret3d.SubGeometry, context3DProxy: egret3d.Context3DProxy, modeltransform: egret3d.Matrix4_4, camera3D: egret3d.Camera3D): any
        {
            if (usage.uniform_time)
            {
                context3DProxy.uniform1f(usage.uniform_time.uniformIndex, this.animTime);
            }
            var mat = this.rootBoneObj.modelMatrix;
            // mat.identity();
            //mat.makeTransform(new egret3d.Vector3D(0, 0, 0), new egret3d.Vector3D(10, 10, 10), new egret3d.Quaternion());
            context3DProxy.uniformMatrix4fv(usage.uniform_ModelMatrix.uniformIndex, false, mat.rawData);
            context3DProxy.uniform4fv(usage.uniform_PoseMatrix.uniformIndex, this._skeletonMatrixData);

        }


        ///下面的接口都是凑数的
        animTime: number = 0;
        delay: number;
        speed: number = 0;
        play(animName?: string, speed?: number, reset?: boolean, prewarm?: boolean): void
        {
        }
        stop(): void
        {

        }
        isPlay(): boolean
        {
            return false;
        }
        animStateNames: string[];
        animStates: egret3d.IAnimationState[];
        addAnimState(animState: egret3d.IAnimationState): any
        {

        }

        removeAnimState(animState: egret3d.IAnimationState): any
        {

        }


    }
    export class AniPlayer
    {
        constructor(node: Entity_Mesh)
        {
            this.root = node;
        }
        root: Entity_Mesh;
        get skeletonAnimationController(): egret3d.SkeletonAnimation
        {
            return null;
        }

        public get curFrameIndex(): number
        {
            return this._playFrameid;
        }

        crossdelta: number = 0;
        crossspeed: number = 0;
        speed: number = 1.0;
        //animStateNames: string[];
        //animStates: egret3d.IAnimationState[];

        //需要调用当前Clip，所以公开
        public _playClip: FreeNode.AniClipData = null;
        //private _playClip: FreeNode.AniClipData = null;
        private _playFrameid: number = 0;
        private _playTimer: number = 0;
        isStop(): boolean
        {
            if (this._playClip == null) return false;
            if (this.playStyle != PlayStyle.NormalPlay) return false;
            if (this._playClip.loop) return false;
            if (this._playFrameid == this._playClip.frames.length - 1)
                return true;
            return false;
        }
        clips: { [id: string]: FreeNode.AniClipData } = {};
        tpose: { [id: string]: FreeNode.PoseBoneMatrix } = {};
        nowpose: { [id: string]: FreeNode.PoseBoneMatrix } = {};
        lerppose: { [id: string]: FreeNode.PoseBoneMatrix } = {};

        carelist: { [id: string]: Entity_Empty | Entity_Mesh } = {};

        private _temp_quat: egret3d.Quaternion = new egret3d.Quaternion();
        private _temp_vec3: egret3d.Vector3D = new egret3d.Vector3D();

        clone(node: Entity_Mesh): AniPlayer
        {
            var ap = new AniPlayer(node);
            ap.crossdelta = 0;
            ap.crossspeed = 0;
            ap.lerppose = {};
            ap.speed = this.speed;
            ap._playClip = this._playClip;
            ap._playFrameid = this._playFrameid;
            ap._playTimer = this._playTimer;
            ap.clips = {};
            for (var c in this.clips)
            {
                ap.clips[c] = this.clips[c];
            }                //cloneneed;
            ap.tpose = {};
            for (var c in this.tpose)
            {
                ap.tpose[c] = this.tpose[c].Clone();
            }                //cloneneed;
            ap.nowpose = {};
            for (var c in this.nowpose)
            {
                ap.nowpose[c] = this.nowpose[c].Clone();
            }                //cloneneed;
            ap.carelist = this.carelist;//needfix
            return ap;
        }

        fix()
        {
            var oldc = this.carelist;
            this.carelist = {};
            for (var key in oldc)
            {
                this.carelist[key] = this.root.findObject3D(key) as Entity_Empty;
            }
        }
        care(node: Entity_Empty | Entity_Mesh)
        {
            var pnode = node;
            while (true)
            {
                if (this.nowpose[pnode.name] != undefined)
                {
                    this.carelist[pnode.name] = pnode;
                    return;
                }
                pnode = <Entity_Empty>pnode.parent;
                if (pnode instanceof (Entity_Empty) || pnode instanceof (Entity_Mesh))
                {
                }
                else
                {
                    break;
                }
            }
        }
        fillPoseData(data: Float32Array, bones: string[]): void
        {
            var seek: number = 0;
            for (var i in bones)
            {
                var key: string = bones[i];
                var obj = this.nowpose[key];
                if (obj == undefined)
                {
                    data[seek * 8 + 0] = 0;//quat.x;
                    data[seek * 8 + 1] = 0;//quat.y;
                    data[seek * 8 + 2] = 0;//quat.z;
                    data[seek * 8 + 3] = 1;//quat.w;
                    data[seek * 8 + 4] = 0;//trans.x;
                    data[seek * 8 + 5] = 0;//trans.y;
                    data[seek * 8 + 6] = 0;//trans.z;
                    data[seek * 8 + 7] = 1;//trans.w;
                }
                else
                {//高效处理，动画信息已经预乘的反的tpose
                    data[seek * 8 + 0] = obj.r.x;
                    data[seek * 8 + 1] = obj.r.y;
                    data[seek * 8 + 2] = obj.r.z;
                    data[seek * 8 + 3] = obj.r.w;
                    data[seek * 8 + 4] = obj.t.x; //trans.x;
                    data[seek * 8 + 5] = obj.t.y; //trans.y;
                    data[seek * 8 + 6] = obj.t.z; //trans.z;
                    data[seek * 8 + 7] = 1;//trans.w;
                }
                seek++;
            }
        }

        /**
        * @language zh_CN
        * 更新调度
        * @param time 总时间
        * @param delay 帧间隔时间
        */
        update(time: number, delay: number): void
        {
            if (this._playClip == null)
                return;
            this.checkFrameId(delay);
                     
            var mix = false;
            if (this.crossdelta > 0)
            {
                this.crossdelta -= delay / 1000.0 * this.speed * this.crossspeed;
                mix = true;
            }

            for (var i = 0; i < this._playClip.boneinfo.length; i++)
            {
                var bone = this._playClip.boneinfo[i];
                //nbone next boneinfo
                //

                var next = this._playClip.frames[this._playFrameid].bonedata[i];
                var outb = this.nowpose[bone];
                var tpose = this.tpose[bone];
                if (outb != undefined)
                {
                    if (mix)
                    {
                        var last = this.lerppose[bone];
                        if (last != undefined)
                        {
                            //把恶心的计算集中提纯到一起去，有空再修改
                            outb.lerpInWorld(tpose, last, next, 1 - this.crossdelta);
                        }
                        else
                        {
                            outb.copyFrom(next);
                        }
                    }
                    else
                    {
                        outb.copyFrom(next);
                    }
                }



                //else//动画里有player我没有的骨骼
                //{//这就需要单独分离出来那些cpu 做tpose处理的骨骼
                //    //if (this.tpose[bone] != undefined)
                //    //{
                //    //    var tb
                //    //    this.nowpose[bone] = FreeNode.PoseBoneMatrix.sMultiply(this.tpose[bone], next);
                //    //    if (mix)
                //    //    {
                //    //        this.lerppose[bone] = this.nowpose[bone].Clone();
                //    //    }
                //    //}
                //}
                var careobj = this.carelist[bone];
                if (careobj != undefined)
                {
                    //tbone ,一串算出最终坐标
                    //把恶心的计算集中提纯到一起去，有空再修改
                    var fmat = FreeNode.PoseBoneMatrix.sMultiply(outb, tpose);
                    var tmat = AniPlayer.toEgretMat(fmat);
                    tmat.multiply(this.root.modelMatrix);
                    careobj.modelMatrix = tmat;

                }
            }
        }

        private checkFrameId(delay: number): void {
            if (this.playStyle == PlayStyle.NormalPlay) {
                this._playTimer += delay / 1000.0 * this.speed;
                this._playFrameid = (this._playClip.fps * this._playTimer) | 0;
                if (this._playClip.loop)//加上循环与非循环动画的分别控制
                {
                    this._playFrameid %= this._playClip.frames.length;
                }
                else if (this._playFrameid > this._playClip.frames.length - 1) {
                    this._playFrameid = this._playClip.frames.length - 1;
                }
                if (this.beRevert) {
                    this._playFrameid = this._playClip.frames.length - this._playFrameid - 1;
                }
                
            } else if (this.playStyle == PlayStyle.FramePlay) {
                //使用传进来的百分比计算当前播放帧
                this._playFrameid = (this._playClip.frames.length * this.percent) - 1;
                this._playFrameid = Math.round(this._playFrameid);
            }
            if (this._playFrameid < 0) {
                this._playFrameid = 0;
            }
            if (this._playFrameid > this._playClip.frames.length - 1) {
                this._playFrameid = this._playClip.frames.length - 1;
            }
            //console.warn(this._playFrameid + "/" + this._playClip.frames.length);
        }
       
        static _convone = new egret3d.Vector3D(1, 1, 1);
        static _convt = new egret3d.Vector3D(0, 0, 0);
        static _convq = new egret3d.Quaternion(0, 0, 0, 1);
        static toEgretMat(src: FreeNode.PoseBoneMatrix, target: egret3d.Matrix4_4 = null): egret3d.Matrix4_4
        {
            AniPlayer._convt.x = src.t.x;
            AniPlayer._convt.y = src.t.y;
            AniPlayer._convt.z = src.t.z;
            AniPlayer._convq.x = src.r.x;
            AniPlayer._convq.y = src.r.y;
            AniPlayer._convq.z = src.r.z;
            AniPlayer._convq.w = src.r.w;
            if (target == null)
                target = new egret3d.Matrix4_4();
            target.makeTransform(AniPlayer._convt, AniPlayer._convone, AniPlayer._convq);
            return target;
        }

        private beRevert: boolean = false;
        private playStyle: PlayStyle = PlayStyle.NormalPlay;
        private percent: number = 0;

        play(animName: string, crosstimer: number = 0.2, speed: number = 1.0, beRevert: boolean = false): void
        {
            if (this.clips[animName] == undefined) return;
            this._playClip = this.clips[animName];
            this._playTimer = 0;
            this._playFrameid = 0;
            this.speed = speed;

            this.beRevert = beRevert;
            this.playStyle = PlayStyle.NormalPlay;

            this.crossspeed = 1.0 / crosstimer;
            this.crossdelta = 1.0;
            this.speed = speed;
            this.lerppose = {};
            //clone  a pose
            for (var key in this.nowpose) {
                var src = this.nowpose[key];
                this.lerppose[key] = src.Clone();
            }
        }
        playAppointPercent(animName: string, percent: number, speed: number = 1.0, beRevert: boolean = false)
        {
            if (this.clips[animName] == undefined) return;
            if (this._playClip == null)
            {
                this._playClip = this.clips[animName];
            }
            this._playTimer = 0;
            this.percent = percent;

            this.speed = speed;
            this.beRevert = beRevert;
            this.playStyle = PlayStyle.FramePlay;
        }
        playReverse(animName: string, crosstimer: number = 0.2, speed: number = 1.0): void
        {
            if (this.clips[animName] == undefined) return;
            this._playClip = this.clips[animName];
            this._playTimer = 0;
            this._playFrameid = 0;
            this.speed = speed;

            this.beRevert = true;
            this.playStyle = PlayStyle.NormalPlay;

            this.crossspeed = 1.0 / crosstimer;
            this.crossdelta = 1.0;
            this.speed = speed;
            this.lerppose = {};
            //clone  a pose
            for (var key in this.nowpose) {
                var src = this.nowpose[key];
                this.lerppose[key] = src.Clone();
            }
        }
        playCross(animName: string, crosstimer: number = 0.2, speed: number = 1.0, beRevert: boolean = false): void
        {
            if (this.clips[animName] == undefined) return;
            this._playClip = this.clips[animName];
            this._playTimer = 0;
            this._playFrameid = 0;

            this.beRevert = beRevert;
            this.playStyle = PlayStyle.NormalPlay;

            this.crossspeed = 1.0 / crosstimer;
            this.crossdelta = 1.0;
            this.speed = speed;
            this.lerppose = {};
            //clone  a pose
            for (var key in this.nowpose)
            {
                var src = this.nowpose[key];
                this.lerppose[key] = src.Clone();
            }
        }
       
        removeBodyPart(part: Entity_Mesh)
        {
            if (part != null && part.parent != null)
            {
                part.parent.removeChild(part);
                part.parent = null;

            }
        }
        addBodyPart(part: Entity_Mesh)
        {
            this.root.addChild(part);
            //this.uploadTpose(part.geometry["tpose"], (part.animation as Skinner).bones);
            (part.animation as Skinner).fix(this.root);
        }
        stop(): void
        {
            this._playClip = null;

        }
        isPlay(): boolean
        {
            return this._playClip != null;
        }
        getAnimList(): string[]
        {
            var anis: string[] = [];
            for (var c in this.clips)
            {
                anis.push(c);
            }
            return anis;
        }
    }
}

enum PlayStyle {
    NormalPlay,
    FramePlay,
    PingPang,
}